package com.cqcis.uip.base.framework.filters.adaptor;

import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.apache.mina.transport.socket.nio.SocketAcceptor;
import org.apache.mina.transport.socket.nio.SocketConnector;

import com.cqcis.uip.base.framework.FrameworkConstants;
import com.cqcis.uip.base.framework.common.ControlMsg;
import com.cqcis.uip.base.framework.service.StreamNameUtil;
import com.cqcis.uip.base.framework.service.StreamOutput;



public abstract class AbstractProtocolObjectEncoder extends ProtocolEncoderAdapter {	
	private StreamNameUtil streamNameUtil;
    private StreamOutput   streamOutput;
    	
	@SuppressWarnings("unchecked")
	public void encode(IoSession session, Object message,
			ProtocolEncoderOutput output) throws Exception {
		if (message instanceof ControlMsg) {
			ControlMsg cm = (ControlMsg)message;
			if (cm.isControlMsg()) {
				output.write(ByteBuffer.wrap(cm.getMessage().getBytes()));				
				return;
			}			
		}
		
		com.cqcis.uip.base.framework.utils.ByteBuffer buffer = FrameworkConstants.VIRTUAL_TRY_TIMEOUT_BUFFERS.contains(message)
		                   ? null : doEncode(message);
		
		if (buffer != null && buffer.hasRemaining()) {
			ByteBuffer mbuffer = buffer.toBuffer();
			int oldpos = buffer.position();			
			saveBufferAsFile(session, mbuffer);			
			buffer.position(oldpos);
						
			output.write(mbuffer);
		}
		
		// 触发一次decode操作
		ByteBuffer buf = (ByteBuffer) session.getAttribute(CumulativeProtocolDecoder.class
	            .getName()
	            + ".Buffer");				
		if (buf != null) {
			ByteBuffer virtualBuffer = ByteBuffer.allocate(4).flip();
			FrameworkConstants.VIRTUAL_NO_DATA_BUFFERS.add(virtualBuffer);
			session.getFilterChain().fireMessageReceived(session, virtualBuffer);
		}		
	}
	
	/**
	 * 将message对象编码成ByteBuffer, 如果出错抛出异常. 返回null, 结果将被忽略
	 * @param message
	 * @return
	 */
	protected abstract com.cqcis.uip.base.framework.utils.ByteBuffer doEncode(Object message) throws Exception;
	
	/**
	 * 保存为文件
	 * 
	 * @param session
	 * @param buffer
	 * @return
	 * @throws Exception
	 */
	private String saveBufferAsFile(IoSession session, ByteBuffer buffer) throws Exception {
		if (session.getAttribute(FrameworkConstants.INTERFACE_STREAM_RECORD_CLOSED) != null) {
			return null;// 字节流功能已经关闭
		}
		// 判断当前session是否写二进制日志
		if (session.getAttribute(FrameworkConstants.STREAM_LOG_NOT_WRITED) != null) {
			// 判断当前session是否写二进制日志
			if (session.getAttribute(FrameworkConstants.STREAM_LOG_NOT_WRITED) != null) {
				session.setAttribute(FrameworkConstants.INTERFACE_STREAM_RECORD, "STREAMLOG WRITE CLOSED");
				return null;
			}
			return null;
		}
		
		String file = null;
		/*server*/
		if (session.getService() instanceof SocketAcceptor) {
			String filePath = (String) session
					.getAttribute(StreamNameUtil.STREAM_FILE_PATH);
			String nameSuffix = (String) session
					.getAttribute(StreamNameUtil.STREAM_NAME_SUFFIX);
            
			if (filePath != null && nameSuffix != null) {
				file = streamOutput.write(filePath,
						streamNameUtil.getSendName(nameSuffix), buffer.buf());
			}
			
			session.removeAttribute(StreamNameUtil.STREAM_FILE_PATH);
			session.removeAttribute(StreamNameUtil.STREAM_NAME_SUFFIX);			
		} else if (session.getService() instanceof SocketConnector) {			
			String filePath = streamNameUtil.getFilePath();		
			session.setAttribute(StreamNameUtil.STREAM_FILE_PATH, filePath);

            if (filePath != null) {
            	file = streamOutput.write(filePath, 
  					  streamNameUtil.getSendName(), buffer.buf());
            }
			
			session.setAttribute(StreamNameUtil.STREAM_NAME_SUFFIX, streamNameUtil.getPreFileSuffix());			
		}/*client*/
		
		session.setAttribute(com.cqcis.uip.base.framework.FrameworkConstants.INTERFACE_STREAM_RECORD, file);
		
		return file;
	}
	
	public void registerStreamNameUtil(StreamNameUtil streamNameUtil) {
		this.streamNameUtil = streamNameUtil;
	}

	public void registerStreamOutput(StreamOutput streamOutput) {
		this.streamOutput = streamOutput;
	}
}
